﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using slTestingStreaming.Framework;
using slTestingStreaming.srPubSub;
using System.Collections.ObjectModel;
using slTestingStreaming.Model.DTO;
using System.ServiceModel;
using System.ComponentModel;

namespace slTestingStreaming.Model.Manager
{
    public class ChatManager : Bindable
    {

        System.Windows.Threading.DispatcherTimer timer_Heart;
        public event EventHandler StatusChanged;
        object syncRoot = new object();
        PubSubClient client = null;

        const int c_heartbeat_mins = 2; //tell the server you are alive
        const string c_PollingService = "http://twitterconference01.cloudapp.net/SilverlightChat/PubSubService.svc";
        //const string c_govhackservice = "http://localhost:59642/SilverlightChat/PubSubService.svc";

        //[Bindable]
        public string Notifications { get; set; }

        //[Bindable]
        public string Topic { get; set; }

        //[Bindable]
        public ObservableCollection<string> ChatItems { get; set; }

        //[Bindable]
        public User User { get; set; }


        public enum eStatus
        {
            Loaded = 0,
            Connected = 1,
            Disconnected = 2,
            Available = 3,
            Unavailable = 4
        }

        //[Bindable]
        public eStatus Status { get; set; }

        //[Bindable]
        //public long ConnectedUserCount { get; set; }

        #region ConnectedUserCount


        public long ConnectedUserCount
        {
            get { return (long)GetValue(ConnectedUserCountProperty); }
            set { SetValue(ConnectedUserCountProperty, value); }
        }

        public static readonly DependencyProperty ConnectedUserCountProperty =
            DependencyProperty.Register("ConnectedUserCount", typeof(long), typeof(ChatManager), new PropertyMetadata(null));


        #endregion

        public ChatManager()
        {
            Topic = "ConferenceTweets";
            ChatItems = new ObservableCollection<string>();
            ConnectedUserCount = 0;
            Status = eStatus.Loaded;
            User = new User();

            //Heartbeat
            //timer_Heart = new System.Windows.Threading.DispatcherTimer();
            //timer_Heart.Interval = TimeSpan.FromMinutes(c_heartbeat_mins);
            //timer_Heart.Tick += new EventHandler(timer_Heart_Tick);
        }


        #region HeartBeat
        public void StartHeart()
        {
            //timer_Heart.Start();
        }
        public void StopHeart()
        {
            //timer_Heart.Stop();
        }

        void timer_Heart_Tick(object sender, EventArgs e)
        {
            client.ClientHeartbeatAsync(this.Topic);
        }
        #endregion

        public bool Connect(string displayName, string lat, string lon)
        {
            if (Status == eStatus.Loaded || Status == eStatus.Disconnected)
            {

                this.client = new PubSubClient(
                    new PollingDuplexHttpBinding(),
                    new EndpointAddress(c_PollingService));
                this.client.NotifyReceived += new EventHandler<NotifyReceivedEventArgs>(client_NotifyReceived);
                this.client.PublishCompleted += new EventHandler<AsyncCompletedEventArgs>(client_PublishCompleted);
                this.client.SubscribeCompleted += new EventHandler<AsyncCompletedEventArgs>(client_SubscribeCompleted);

                this.client.SubscribeAsync(this.Topic, displayName, lat, lon);

                Status = eStatus.Connected;

                this.User.UserName = displayName;
                this.User.Lat = lat;
                this.User.Lon = lon;
                this.NotifyPropertyChanged("User");

                if (StatusChanged != null) StatusChanged(null, null);

                return true;
            }
            else return false;
        }

        public bool Disconnect()
        {
            if (Status == eStatus.Connected || Status == eStatus.Available || Status == eStatus.Unavailable)
            {

                this.client.UnsubscribeAsync(this.Topic);

                this.client.NotifyReceived -= client_NotifyReceived;
                this.client.PublishCompleted -= client_PublishCompleted;
                this.client.SubscribeCompleted -= client_SubscribeCompleted;

                this.client = null;

                Status = eStatus.Disconnected;
                this.ConnectedUserCount = 0;
                if (StatusChanged != null) StatusChanged(null, null);

                return true;
            }
            else return false;
        }

        public bool Available(string displayName)
        {
            if (Status == eStatus.Connected || Status == eStatus.Unavailable)
            {
                this.client.AvailableAsync(this.Topic, displayName);

                Status = eStatus.Available;

                this.User.UserName = displayName;
                this.NotifyPropertyChanged("User");

                if (StatusChanged != null) StatusChanged(null, null);

                return true;
            }
            else return false;


        }

        public bool Unavailable(string displayName)
        {
            if (Status == eStatus.Available || Status == eStatus.Connected)
            {
                this.client.UnavailableAsync(this.Topic, displayName);

                Status = eStatus.Unavailable;

                if (StatusChanged != null) StatusChanged(null, null);

                return true;
            }
            else return false;
        }

        public void PublishText(string content)
        {

            lock (this.syncRoot)
            {
                if (this.client != null)
                {
                    this.client.PublishAsync(this.Topic, content, content);
                }
            }

        }


        void client_SubscribeCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (!this.IsError(e))
            {
                //string msg = e.request.GetBody<NotificationData>().Content;
                //string partA = msg.Substring(0, 3);
                //string partB = msg.Substring(3, msg.Length - 3);


                //string[] parts = partB.Split("^".ToCharArray());

                //this.User.Name = parts[0];
                //this.User.Lat = parts[1];
                //this.User.Lon = parts[2];
                //this.User.SessionID = parts[3];

                this.StartHeart();
            }
        }

        void client_PublishCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (!this.IsError(e))
            {
                this.AddNotification("CLIENT ACTION: Published to topic " + this.Topic + ": " + e.UserState);
            }
        }

        void client_NotifyReceived(object sender, NotifyReceivedEventArgs e)
        {
            if (!this.IsError(e))
            {
                string msg = e.request.GetBody<NotificationData>().Content;
                string partA = msg.Substring(0, 3);
                string partB = msg.Substring(3, msg.Length - 3);

                if (partA == "050" || partA == "020") //"user count" or connected 
                {
                    string[] parts = partB.Split("^".ToCharArray());

                    this.ConnectedUserCount = long.Parse(parts[0]);
                }
                else if (partA == "070") //user detail changed
                {
                    string[] parts = partB.Split("^".ToCharArray());

                    //this.User.Name = parts[0];
                    //this.User.Lat = parts[1];
                    //this.User.Lon = parts[2];
                    //this.User.SessionID = parts[3];
                }
                else if (partA == "001") //generic message
                {
                    this.AddNotification(partB);
                }



            }
        }

        bool IsError(AsyncCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                lock (this.syncRoot)
                {
                    this.client.CloseAsync();
                    this.client = null;
                }
                this.AddNotification("ERROR: " + e.Error.Message);
                //this.TopicName.IsEnabled = this.SubscribeButton.IsEnabled = true;
                //this.PublishText.IsEnabled = false;
            }

            return e.Error != null;
        }

        void AddNotification(string notification)
        {
            //this.Notifications += notification + Environment.NewLine;
            ChatItems.Add(notification);

        }


    }
}
